/* 
防抖（debounce）：每次触发定时器后，取消上一个定时器，然后重新触发定时器。防抖一般用于用户未知行为的优化，比如搜索框输入弹窗提示，因为用户接下来要输入的内容都是未知的，所以每次用户输入就弹窗是没有意义的，需要等到用户输入完毕后再进行弹窗提示。
防抖可以想象成回城，当操作被打断之后就需要重新计时了

节流（throttle）：每次触发定时器后，直到这个定时器结束之前无法再次触发。一般用于可预知的用户行为的优化，比如为scroll事件的回调函数添加定时器。
节流就想象成是释放技能，当释放了技能之后就回进入冷却，不会再次执行了
*/
// 基础版本的防抖函数
function debounce(callback, delay = 300) {
  let timer = null;

  return (...args) => {
    if (timer) clearTimeout(timer); // 判断是否已经包含了定时器，是，就清除定时器

    timer = setTimeout(() => {
      callback(...args);
      timer = null;
    }, delay);
  };
}

// 立即执行版本的防抖函数
function debounce(callback, delay = 300, immediate = false) {
  let timer = null;

  const debounced = (...args) => {
    if (timer) {
      clearTimeout(timer);
    }

    if (immediate && !timer) {
      // 如果是立即执行模式，并且没有定时器，则立即执行回调
      callback(...args);
    }

    // 设置新的定时器
    timer = setTimeout(() => {
      if (!immediate) {
        // 如果不是立即执行模式，则在延迟后执行回调
        callback(...args);
      }
      timer = null; // 清除定时器
    }, delay);
  };

  // 提供一个取消函数，方便在组件销毁的时候调用
  debounced.cancel = () => {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
  };

  return debounced;
}

// 基础版本的节流函数
function throttle(callback, delay = 300) {
  let timer = null;

  return (...args) => {
    if (timer) return;

    timer = setTimeout(() => {
      callback(...args);
      timer = null; // 清除定时器
    }, delay);
  };
}

// 包含立即执行版本的节流函数，截留函数的立即执行版本好像不是非常的常见
function throttle(callback, delay = 300, immediate = false) {
  let timer = null;
  let lastArgs = null;
  let shouldCallAtEnd = false;

  const throttled = (...args) => {
    if (timer) {
      // 如果已经有定时器在运行，则记录最后一次调用的参数
      lastArgs = args;
      shouldCallAtEnd = true;
      return;
    }

    // 立即执行模式下，立即执行回调
    if (immediate) {
      callback(...args);
    }

    // 设置新的定时器
    timer = setTimeout(() => {
      if (shouldCallAtEnd && !immediate) {
        // 如果设置了尾部执行且不是立即执行模式，则在延迟后执行最后一次调用
        callback(...lastArgs);
      }
      timer = null; // 清除定时器
    }, delay);
  };

  // 提供一个取消函数，方便在组件取消的时候调用
  throttled.cancel = () => {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
  };

  return throttled;
}
